package com.fi.recogn;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.fi.common.domain.entity.ResAccessoryFileInfo;
import com.fi.common.domain.vo.FileData;
import com.fi.common.service.OssService;
import com.fi.common.service.ResAccessoryFileInfoService;
import com.fi.recogn.dto.RecognReq;
import com.fi.recogn.util.FileTypeUtil.FileType;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @Description TODO 读取excel表格 抽象类
 * @Author lujiahong
 * @Date 2024/8/12 12:40
 */
@Slf4j
@Component
public abstract class AbstractRecogn<T extends FileData> implements ApplicationContextAware {
    //只初始化工厂一次
    private static boolean isInitFactory = false;
    //识别工厂
    private final Map<FileType, AbstractRecogn<T>> RECOGN_FACTORY = new HashMap<>();
    private final String recognName;//识别器名称
    private final StopWatch stopWatch;//耗时监控

    public AbstractRecogn(String recognName, StopWatch stopWatch){
        this.recognName = recognName;
        this.stopWatch = stopWatch;
    }

    @Resource
    protected OssService ossService;//对象云存储服务
    @Resource
    protected ResAccessoryFileInfoService resAccessoryFileInfoService;//公告信息服务

    @Override
    @SuppressWarnings("unchecked")
    public void setApplicationContext(@NotNull ApplicationContext ac) {
        if(isInitFactory)return;
        isInitFactory = true;
        String[] beanNamesForType = ac.getBeanNamesForType(AbstractRecogn.class);
        for (String beanName : beanNamesForType) {
            AbstractRecogn<T> recogn = ac.getBean(beanName, AbstractRecogn.class);
            RECOGN_FACTORY.put(recogn.getFileType(), recogn);
        }
        log.info("识别工厂初始化完成，共{}个识别器", RECOGN_FACTORY.size());
    }

    // 识别器执行入口
    public FileData exec(RecognReq req){
        Assert.notNull(req,"识别请求不能为空");
        Assert.isFalse(ObjUtil.isNull(req.getAccessId()) && ObjUtil.isNull(req.getInfo()),"公共id和公告信息不能同时为空");
        if(ObjUtil.isNotNull(req.getAccessId()) && ObjUtil.isNull(req.getInfo())){
            ResAccessoryFileInfo info = resAccessoryFileInfoService.getById(req.getAccessId());
            Assert.notNull(info,"公告信息不存在");
            req.setInfo(info);
        }
        return this.exec(req,data-> RECOGN_FACTORY.get(data.getFileType()).recogn(data));
    }

    // 识别器执行入口
    public FileData exec(RecognReq req, Function<T,FileData> function){
        return function.apply(ossService.parseCommon(req));
    }

    // 识别逻辑抽象方法
    public abstract FileData recogn(T data);
    // 文件类型抽象方法
    public abstract FileType getFileType();

    protected void startLog(FileData data){
        log.info("【（{}）-（{}）】开始识别公告：{} - {} - {} bytes",data.getId(), recognName,data.getFileName(),data.getFileType(),data.getFileSize());
        stopWatch.start();
    }

    protected void endLog(FileData data){
        stopWatch.stop();
        log.info("【（{}）-（{}）】识别公告结束：{} - {} - {} bytes,耗时：{}ms",data.getId(), recognName,data.getFileName(),data.getFileType(),data.getFileSize(),stopWatch.getTotalTimeMillis());
    }
}